#include "query_handler.h"
#include <libpq-fe.h>
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <vector>
#include <string>

using kwdb::ingest::v1::QueryReq;
using kwdb::ingest::v1::QueryResp;
using kwdb::ingest::v1::QueryRow;

static const char* env(const char* n, const char* dfl) {
  const char* v = getenv(n);
  return (v && *v) ? v : dfl;
}

// Create a new libpq connection (thread-safe: each request gets its own)
static PGconn* create_query_conn(std::string* err) {
  std::ostringstream ci;
  ci << "host=" << env("KWDB_HOST", "127.0.0.1")
     << " port=" << env("KWDB_SQL_PORT", "26257")
     << " dbname=" << env("KWDB_DB", "kwdb")
     << " user=" << env("KWDB_USER", "root")
     << " sslmode=disable";

  PGconn* conn = PQconnectdb(ci.str().c_str());
  if (PQstatus(conn) != CONNECTION_OK) {
    if (err) *err = PQerrorMessage(conn);
    PQfinish(conn);
    return nullptr;
  }
  return conn;
}

// Execute SQL query using libpq and parse result
static bool exec_query(const std::string& sql, QueryResp* resp) {
  std::string err;
  PGconn* conn = create_query_conn(&err);
  if (!conn) {
    resp->set_code(1);
    resp->set_message("Connection failed: " + err);
    return false;
  }

  // Execute query
  PGresult* result = PQexec(conn, sql.c_str());
  if (PQresultStatus(result) != PGRES_TUPLES_OK) {
    resp->set_code(2);
    resp->set_message(PQerrorMessage(conn));
    PQclear(result);
    PQfinish(conn);
    return false;
  }

  // Get column info
  int nfields = PQnfields(result);
  int ntuples = PQntuples(result);

  // Process results
  for (int i = 0; i < ntuples; i++) {
    auto* row = resp->add_rows();

    // Add column names (only needed once per row in this proto structure)
    for (int j = 0; j < nfields; j++) {
      row->add_columns(PQfname(result, j));
    }

    // Add values
    for (int j = 0; j < nfields; j++) {
      if (PQgetisnull(result, i, j)) {
        row->add_values("NULL");
      } else {
        row->add_values(PQgetvalue(result, i, j));
      }
    }
  }

  PQclear(result);
  PQfinish(conn);

  resp->set_code(0);
  resp->set_message("ok");
  return true;
}

grpc::Status QueryServiceImpl::Query(grpc::ServerContext*,
                                     const QueryReq* req,
                                     QueryResp* resp) {
  if (!req || req->sql().empty()) {
    resp->set_code(1);
    resp->set_message("empty sql");
    return grpc::Status::OK;
  }

  std::cout << "[query_handler] executing: " << req->sql() << std::endl;

  exec_query(req->sql(), resp);

  std::cout << "[query_handler] result: code=" << resp->code()
            << ", rows=" << resp->rows_size() << std::endl;

  return grpc::Status::OK;
}